אוניברסיטת בן-גוריון מדור בחינות מספר נבחן: רשמו תשובותיכם בגיליון התשובות בלבד תשובות מחוץ לגיליון לא יבדקו בהצלחה! תאריך הבחינה: 2222010 שם המורה: ד"ר מיכאל אלחדד ד"ר מני אדלר מר אוריאל ברגיג שם הקורס: תכנות מערכות מספר הקורס: 202-1-2031 מיועד לתלמידי: מדעי המחשב, הנדסת תוכנה שנה: תש"ע סמסטר: א' מועד: ב' משך הבחינה: שלש שעות חומר עזר: אסור 30) שאלה 1 נקודות) בשאלה זו נמשיך לעסוק במערכת לטיהור המים בה עסקנו במועד א' המערכת מורכבת, כזכור, מהאובייקטים הפסיביים הבאים: Pool (WaterPool - עבור מים מטוהרים ועבור מים משומשים - המממשים את הממשק WaterPool) שני מיכלים - המחזירה getquality() Purifier בממשק מוגדרת המתודה (WaterPurifier המממש את הממשק מטהר מים ) - המקבלת כמות של מים משומשים purify את כמות המים באחוזים שניתן לטהר באיכות טובה והמתודה ומחזירה כמות של מים מטוהרים בסימולציה רצים 3 אובייקטים אקטיביים: : הוספת מים למיכל המים המטוהרים (למשל זרימת ProducingTask :Producer ת'רד הרץ מעל המשימה - המים מהקו העירוני לצנרת הבית) : צריכת מים ממיכל המטהורים והעברתם בתום ConsumingTask :Consumer ת'רד הרץ מעל המשימה - השימוש למיכל המים המשומשים (למשל צרכן הפותח ברז, צורך מים, והמים מתנקזים למאגר המים המיועדים לטיהור) : לקיחת מים ממיכל המים המשומשים, טיהורם בעזרת Purifying ngtask : Purifier ת'רד הרץ מעל המשימה - מטהר המים, והוספתם למיכל המים המטוהרים (כמו פעולת המערכת לטיהור המים האפורים) Purifier Water Purifier Used Water Purified Water Consumer Producer הקוד המקורי של המערכת, בדיוק כפי שהופיע במועד א', ניתן בנספח א' א במועד א' נוכחנו לדעת כי המערכת בטוחה האם ההרצה של המערכת מקיימת נכונות? (כזכור, הרצת מערכת תוגדר כנכונה, אם לכל תוצאה של ביצוע מקבילי קיימת תוצאה סדרתית מתאימה, בסדר כל שהוא של פעולות) נמקו [5 נקודות] 1
ב ג ד הסבירו מדוע ביצוע interrupt() על כל אחד משלושת הת'רדים במערכת, לא יגרום בהכרח להפסקת וכן אין לשנות את Systemexit exit ריצתו ועדכנו את הקוד כך שעבודתו תופסק בכל מקרה אין להשתמש ב WaterPool 10] נקודות] חתימות המתודות ב הסבירו מדוע ה,Producer הממתין להתרוקנות מיכל המים המטוהרים, יתעורר גם אם נוספו מים למיכל על ידי ה?Purifier [3 נקודות] כך שת'רד הממתין לירידה בכמות המים לא יתעורר בעקבות הוספת מים WaterPool עדכנו את המחלקה על ידי ת'רד אחר וכן להפך, ת'רד הממתין לתוספת מים לא יתעורר בעקבות הורדת מים על ידי ת'רד אחר רמז: השתמשו במוניטורים שונים עבור ההמתנות [12 נקודות] 30) שאלה 2 נקודות) נתונות המחלקות הבאות המייצגות מחסנית של אירועים הקוד תקין: class Event { public: Event(int id) : _id(id) {; virtual ~Event() { private: const int _id; ; class SellingEvent : public Event { public: SellingEvent(int id, int price) : Event(id), _price(price) {; virtual int price() {return _price; private: int _price; ; // NOTE: nodes are double linked with next and prev // all field are public class EventNode { public: EventNode(){; Event *data; EventNode *next; EventNode *prev; ; // EventsStack manages a double linked stack based on EvenNode class EventsStack { private: EventNode *_top; 2
EventNode *_last; public: EventsStack(); void push(event *data); void pop(); ; EventsStack::EventsStack():_top(0),_last(0){; // Note: take your time to understand how push works void EventsStack::push(Event *e) { EventNode *q = new EventNode(); q->data = e; q->next = _top; q->prev = 0; if (_top!= 0) _top->prev = q; _top = q; if (_last == 0) _last = q; בסעיפים א-ב יש לצייר את תמונת הזיכרון של התהליך הכוללת ערכים הנמצאים במחסנית, ערכים הנמצאים stack ("נמחקו pointer vtables אין לאייר ערכים שמתחת ל אם יש אין לאייר את ה vtable בערימה ומצביעי מהמחסנית") כל סעיף יש לאייר מחדש לכל תא זיכרון באיור יש לכלול מספר המייצג את כתובתו ומספר המייצג את ערכו הוא 4 נקבע כי כתובות בערימה הם בתחום 1000 עד 3000 וכי כתובת המחסנית int גודלם של מצביעים ו מתחילה ב 5000 ועולה כשהמחסנית גדלה א ציירו את תמונת הזיכרון לאחר ביצוע השורות הבאות [7 נקודות] void main() { EventsStack eventstack; Event e1(1); eventstackpush(&e1); ב ציירו את תמונת הזיכרון לאחר ביצוע השורות הבאות [8 נקודות] void main() { EventsStack eventstack; Event *e1 = new Event(1); eventstackpush(e1); SellingEvent e2(2,20); eventstackpush(&e2); 3
EventsStack פעולת ההעתקה צריכה להיות עמוקה מחסנית האירועים ג הוסיפו מימוש לבנאי מעתיק עבור כך שיווצרו הצבעות כפולות ל Event -ים ניקוד מלא יינתן EventNodes צריכה להיות מועתקת כולל כל ה- לפתרון אלגנטי (כ - 4 שורות קוד) [7 נקודות] void EventsStack:: ::pop pop() ד הוסיפו את המימוש לשיטה next,_,_top top,_,_last לעדכון ערכי [8 נקודות] המוציאה את האיבר הראשון מהמחסנית יש לשים לב במידת הצורך במקרים בהם יש איבר יחיד או במקרים בהם יש מספר אברים prev,next 30) שאלה 3 נקודות),Stomp בשאלה זו נמשיך לעסוק במערכת ממועד א', תהליך ביניים המספק ממשקי RMI המפשטת את העבודה של הלקוח מול ה לשרת בעזרת StompClient 1 StompClientRMI StompServer StompConnector_STUB StompOperator_STUB 1 StompClient n subscribe unsubscribe send getmessages connect StompConnector StompOperator 1 StompOperator n CONNECT MESSAGE SUBSCRIBE UNSUBSCRIBE SEND MESSAGE StompOperator_STUB n connect StompConnector_STUB rebind lookup rmiregistry StompConnector_STUB RMI Connection TCP Connection הקוד של המערכת מהפיתרון של מועד א' ניתן בנספח ב' 4
getmessages() המממש את במימוש הנוכחי משיכה של ההודעות שהגיעו עד כה נעשית באופן יזום, על ידי הפעלת המתודה StompOperator של () כעת, אנו מעוניינים לקבל הודעות מהשרת באופן שוטף, מבלי לקרוא 'מידי פעם' למתודה (StompClient StompClient) כדי לממש זאת, הציע אחד המתרגלים בקורס, להגדיר אצל הלקוח message MessageReceiver ממשק זה מאפשר להעביר הודעה על ידי הפעלת המתודה הממשק getmessages() RemoteObject public interface MessageReceiver extends javarmiremote { void message(string str) throws javarmiremoteexception;,messagereceiver (StompClient רוצה להתחבר לשרת, הוא מייצר מראש אובייקט המממש StompClient) כאשר לקוח connect כפרמטר למתודה, על ידי הפעלת המתודה StompClientRMI הודעות מהשרת יועברו ללקוח ב המתודה הינו למעשה תגובת הלקוח להודעה שנקבלה) שונתה בהתאם - פרמטר נוסף מסוג StompConnector בממשק connect חתימת המתודה של אובייקט זה ומעבירו (מימוש : MessageReceiver message public interface StompConnector extends javarmiremote { StompOperator connect(string login, String passcode,, MessageReceiver messagereceiver) throws javarmiremoteexception,ioexception; : StompOperator getmessages() כעת לא נדרשת יותר המתודה בממשק public interface StompOperator extends javarmiremote { void subscribe(string groupname) throws javarmiremoteexception,ioexception; void unsubscribe(string groupname) throws javarmiremoteexception,ioexception; void send(string groupname, String str) throws javarmiremoteexception,ioexception; List<String> getmessages() throws javarmiremoteexception,ioexception; להלן מימוש של ממשקים אלו: public class MessageReceiverImp extends javarmiserverunicastremoteobject implements MessageReceiver { MessageReceiverImpl() throws javarmiremoteexception { public void message(string str) throws javarmiremoteexception ion { Systemoutprintln(str); public class StompConnectorImpl extends javarmiserverunicastremoteobject implements StompConnector { protected String _stompserverhost; protected int _stompserverport; public StompConnectorImpl(String tring stompserverhost, int stompserverport) throws javarmiremoteexception { _stompserverhost = stompserverhost; 5
_stompserverport = stompserverport; public StompOperator connect (String login, String passcode, MessageReceiver messagereceiver Receiver) throws javarmiremoteexception,ioexception { Socket socket = new Socket(_stompServerHost, _stompserverport); PrintWriter out = new PrintWriter(new OutputStreamWriter(socketgetOutputStream(),"UTF-8")); outprint("connect ONNECT\nlogin: " + login + "\npasscode:" " + passcode + "\n\n"" n" + '\0');' return new StompOperatorImpl(socketgetInputStream(),socketgetOutputStream(),messageReceiver messagereceiver); StompOperatorImpl א עדכנו את הקוד המקורי של המחלקה ההודעות מהשרת [6 נקודות] כך שיממש את האופן החדש של קבלת StompClient ב עדכנו את הקוד ב StompClient כך שיתאים למערכת החדשה יש לדאוג לכך שההודעות המתקבלות מהשרת יודפסו על המסך של התהליך קבלתן [6 נקודות] מיד עם ג ד עדכנו את הקוד מסעיף ב' (אם לא עניתם על סעיפים א-ב, התייחסו לקוד המקורי), כך שבתגובה לקבלת,(StompClient הודעת תגובה, המאשרת את קבלת ההודעה: StompClient) הודעה, י ש ל ח מהזיכרון של תהליך הלקוח " ניתן לשנות את חתימת המתודות בממשקים [10 נקודות] Got your message: <received message> " ציינו נכון/ לא נכון עבור הקביעות הבאות: I הוספת תהליכי הביניים* יקרה יותר מבחינת מספר פעולות תקשורת II הוספת תהליכי הביניים* מורידה את רמת האמינות של שליחת ההודעות III מימוש ה StompServer עם reactor חוסך משאבי זיכרון עבור מספר רב של לקוחות IV מימוש ה StompServer עם reactor משרת באופן הוגן יותר מספר רב של לקוחות * תהליכי הביניים הם ClientRMI, rmiregistry שנוספו למערכת המקורית של תרגיל 3, שהתבססה רק על StompClient ו StompServe [8 נקודות] 6
10) שאלה 4 נקודות) במועד א' בנינו מודל נתונים עבור לוח זמני הטיסות של נמל התעופה בן גוריון כזכור, לכל טיסה יש מספר זיהוי יחודי בנוסף, מתאפיינת הטיסה על פי היעד שלה, מספר הטרמינל, שער היציאה, תאריך ושעת הטיסה, והמטוס המבצע את הטיסה מטוס מתאפיין על פי מספר יחודי, וציון הסוג שלו לכל סוג מטוס יש מחרוזת המתארת את המפרט הטכני שלו, וכן שדה המציין את קיבלת הנוסעים המקסימאלית להלן תאור של המודל כטבלאות ומפתחות ב :SQL CREATE TABLE PlaneModels ( Model varchar(20) PRIMARY KEY, TechnicalSpec varchar(10000) Capacity integer) i CREATE TABLE Planes ( ID integer PRIMARY KEY, Model varchar(20) FOREIGN KEY REFERENCES PlaneModels(Model)) CREATE TABLE Flights ( ID integer PRIMARY KEY, Destination varchar(100), Terminal varchar(20), ExitGate varchar(20), ExitDate date, ExitTime time, PlaneId integer FOREIGN KEY REFERENCES Planes(ID)) א - - - הרחיבו את מודל הנתונים כך שיכלול את הנתונים הבאים: שם הטייס המטיס את המטוס תאריך הטיפול הבא של המטוס קיבולת מחלקת העסקים [6 נקודות] ב כתבו שאילתת SQL המחזירה את מספרי הטיסות ואת קיבולת מחלקת העסקים שלהם, היעדים בסדר עולה [4 נקודות] ממוינים על פי 7
נספח א': הקוד המקורי של המערכת של השאלה הראשונה, כפי שהופיע במועד א' interface Pool { long getcapacity(); float getcontent(); float remove(); void add(float addition); class WaterPool implements Pool { private final long _capacity; private float _content; WaterPool(float content, long capacity) { _content = content; _capacity = capacity; public long getcapacity() { return _capacity; public synchronized float getcontent () { return _content; public synchronized float remove() { while (_content == 0) try { wait(); catch (InterruptedException e) { return 0; float ret = _content; _content = 0; notifyall(); return ret; public synchronized void add(float addition) { while (_content + addition > _capacity) try { wait(); catch (InterruptedException e) { return; _content += addition; notifyall(); interface Purifier { float getquality(); float purify(float capacity); class s WaterPurifier implements Purifier { private final float _quality; WaterPurifier(float quality) { _quality = quality; public float getquality() { return _quality; public float purify (float content) { 8
try { Threadsleep((long)content); t); // sleep to simulate using the water catch (InterruptedException e) { return 0; return _quality * content; class ProducingTask implements Runnable { private Pool _purifiedpool; ProducingTask (Pool purifiedpool) { _purifiedpool = purifiedpool; public void run() { while (true) _purifiedpooladd(_purifiedpoolgetcapacity()); class PurifyingTask implements Runnable { private Pool _usedpool, _purifiedpool; private Purifier _purifier; ier; PurifyingTask (Pool usedpool, Pool purifiedpool, Purifier purifier) { _usedpool = usedpool; _purifiedpool = purifiedpool; _purifier = purifier; public void run() { while (true) _purifiedpooladd(_purifierpurify(_usedpoolremove())); edpoolremove())); class ConsumingTask implements Runnable { private Pool _purifiedpool, _usedpool; ConsumingTask (Pool purifiedpool, Pool usedpool) { _purifiedpool = purifiedpool; _usedpool = usedpool; public void run() { while (true) { float w = _purifiedpoolremove(); try { Threadsleep((long)w); catch (InterruptedException e) { return; _usedpooladd(w); class Test { public static void main(string[] args) { 9
Pool purifiedwaterpool = new WaterPool(0, 1000); Pool usedwaterpool = new WaterPool(0, 1000); Purifier waterpurifier = new WaterPurifier(08 8); new Thread(new ProducingTask(purifiedWaterPool))start(); new Thread(new PurifyingTask(usedWaterPool, ol, purifiedwaterpool, waterpurifier))start(); new Thread(new ConsumingTask(purifiedWaterPool, usedwaterpool))start(); נספח ב': הקוד המקורי של המערכת של השאלה השלישית, כפי שהופיע בפיתרון מועד א' public interface StompConnector extends javarmiremote { StompOperator connect(string login, String passcode) throws javarmiremoteexception,ioexception; public interface StompOperator extends javarmiremote { void subscribe(string groupname) throws javarmiremoteexception,ioexception; void unsubscribe(string groupname) throws javarmiremoteexception,ioexception; void send(string groupname, String str) throws javarmiremoteexception,ioexception; List<String> getmessages() throws javarmiremoteexception,ioexception; n; public class StompConnectorImpl extends javarmiserverunicastremoteobject implements StompConnector { protected String _stompserverhost; protected int _stompserverport; public StompConnectorImpl(String stompserverhost, int stompserverport) ort) throws javarmiremoteexception { _stompserverhost = stompserverhost; _stompserverport = stompserverport; public StompOperator connect (String login, String passcode) throws javarmiremoteexception,ioexception { Socket socket = new Socket(_stompServerHost, _stompserverport); PrintWriter out = new PrintWriter(new OutputStreamWriter(socketgetOutputStream(),"UTF-8")); outprint("connect\nlogin: nlogin: " + login + "\npasscode:" " + passcode + "\n\n"" n" + '\0');' return new StompOperatorImpl(socketgetInputStream(),socketgetOutputStream()); public class StompOperatorImpl extends javarmiserverunicastremoteobject, implements StompOperator { 10
protected PrintWriter _writer; protected BufferedReader _reader; private List<String> _msgs; // Define a listener task to receive the messages from the Stomp server private class Listener implements Runnable { private MessageTokenizer _tok; public Listener() { _tok = new MessageTokenizer(_reade eader,, '\0'); ' public void run() throws IOException { while (_tokisalive()) ( synchronize (_msgs)( { _msgsadd( msgsadd(_toknexttoken()); public StompOperatorImpl(InputStream in, OutputStream out) throws javarmiremoteexception, eption, IOException { _reader = new BufferedReader(new InputStreamReader(in,"UTF-8")); _writer = new PrintWriter(new OutputStreamWriter(out,"UTF-8")); _msgs = new ArrayList<String>(); // Construct listener new Thread(new Listener(_reader))start(); public void subscribe (String group) throws javarmiremoteexception, IOException { synchronized (_writer( _writer) ) { _writerprint("subscribe\ndestination: ndestination: " + group + "\n\n"" n" + '\0');' public void unsubscribe (String group) throws javarmiremoteexception, IOException { synchronized (_writer( _writer) ) { _writerprint("unsubscribe\ndestination: ndestination: " + group+ "\n\n"" n" + '\0');' public void send (String group, String str) throws javarmiremoteexception, tion, IOException { synchronized (_writer( _writer) ) { _writerprint("send\ndestination: ndestination: " + group+ "\n\n"" " + str + "\n" " + '\0');' public List<String> getmessages() throws javarmiremoteexception { // Snapshot copy of the messages and reset et it List<String> res; synchronized (_msgs)( { res = new ArrayList<String>(_msgs); _msgsclear(); // reset the accumulator to receive new messages return res; 11
public class StompClientRMI { public static s void main(string[] args) { try { // Get the address IP/port of the Stomp server from args String stomphost = args[0]; int port = IntegerparseInt(args[1]); StompConnector c = new StompConnectorImpl (stomphost, port); // Publish the connector to the rmiregistry at the given address Namingrebind("rmi://1322358:2010/StompConnector", c); catch (Exception e) { eprintstacktrace(); public class StompClient { public static void main(string[] args) { try { StompConnector c = (StompConnector ( StompConnector)Naminglookup("rmi://1322358:2010/StompConnector"); StompOperator s = cconnect("user", "password"); ssubscribe("q1"); ssubscr subscribe("q2"); ssend("q3", "Suzy Surprise"); Threadsleep(60000); for r (String s : cgetmessages()) Systemoutprintln(s); catch (Exception e) { eprintstacktrace(); 12